package org.yylnb.openplatform.search.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.yylnb.openplatform.search.service.SearchService;
import org.yylnb.openplatform.search.utils.SearchUtil;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author RainLin
 * @date 2020/8/1 - 11:17
 */
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private ObjectMapper objectMapper;


    @Value("${spring.elasticsearch.index}")
    private String index;
    @Value("${spring.elasticsearch.type}")
    private String type;


    @Override
    public void createIndexAndType(String index, String type) throws Exception {
        if (!isExist(index)) {
            CreateIndexRequest request = new CreateIndexRequest(index);
            request.settings(Settings.builder().put("number_of_shards", "5").put("number_of_replicas", "2").build());
            SearchUtil.buildMapping(request, type);
            client.indices().create(request, RequestOptions.DEFAULT);

        }
    }

    @Override
    public boolean isExist(String index) throws Exception {
        GetIndexRequest request = new GetIndexRequest();
        request.indices(index);
        return client.indices().exists(request, RequestOptions.DEFAULT);
    }

    @Override
    public void add(String json) throws Exception {
        IndexRequest indexRequest = new IndexRequest(index, type);
        indexRequest.source(json, XContentType.JSON);
        client.index(indexRequest, RequestOptions.DEFAULT);
    }

    @Override
    public List<Map> search(String jsonParam) throws Exception {
        //我们的查询可能会分比较多的情况, 比如根据不同的条件查询, 可能还会有高亮查询,如果有高亮,还得知道怎么亮(前后缀)
        //我们约定一下,如果要查询高亮,需要传递关键字,以及高亮参数过来,关键字我们要求通过requestContent传递,高亮的前后缀通过highLightPreTag,highLightPostTag
        SearchRequest searchRequest = new SearchRequest(index);
        List<Map> list = new ArrayList<>();
        boolean isHighLight = false;//默认不开启高亮
        if (StringUtils.isEmpty(jsonParam)) {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.from(0);
            searchSourceBuilder.size(10);
            searchRequest.source(searchSourceBuilder);
        } else {
            Map paramsMap = objectMapper.readValue(jsonParam, Map.class);
            SearchSourceBuilder searchSourceBuilder = SearchUtil.getSearchSourceBuilder(paramsMap);
            int start = (int) paramsMap.get("start");
            int rows = (int) paramsMap.get("rows");
            searchSourceBuilder.from(start);
            searchSourceBuilder.size(rows);///设置分页信息
            String requestContent = (String) paramsMap.get("requestContent");
            if (requestContent != null) {
                //开始获取高亮的前后缀
                String highLightPreTag = (String) paramsMap.get("highLightPreTag");
                String highLightPostTag = (String) paramsMap.get("highLightPostTag");
                HighlightBuilder highlightBuilder = new HighlightBuilder();
                highlightBuilder.requireFieldMatch(false).field("content").numOfFragments(0).preTags(highLightPreTag).postTags(highLightPostTag);
                searchSourceBuilder.highlighter(highlightBuilder);//设置高亮
                isHighLight = true;
            }

            searchRequest.source(searchSourceBuilder);
        }


        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit searchHit : searchHits) {
            Map<String, Object> map = searchHit.getSourceAsMap();
            //用高亮数据替换掉原始数据
            if (isHighLight) {
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                HighlightField highlightField = highlightFields.get("content");
                if (highlightField != null) {
                    Text[] fragments = highlightField.fragments();
                    if (fragments != null) {
                        String highlightString = fragments[0].toString();//获取高亮的字符串
                        map.put("content", highlightString);//替换掉内容
                    }
                }
            }

            list.add(map);
        }
        return list;
    }


    @Override
    public Long getCount(String jsonParam) throws Exception {
        SearchSourceBuilder searchSourceBuilder = null;
        SearchRequest searchRequest = new SearchRequest(index);
        if (StringUtils.isEmpty(jsonParam)) {
            //我们期望的是根据我们的查询条件返回总条数,如果没有设置查询条件,就是查询所有的数据的数量,怎么得到结果
            //我们发现即便是在聚合函数的情况下仍旧会返回响应的数据,所以我们可以直接不需要聚合函数,直接插结果就行了
            searchSourceBuilder = new SearchSourceBuilder();
        } else {

            //如果设置了查询条件,怎么统计综合的数量,因为我们要算数量,需要一个列,这个列在查询结果中的值可能会有很多,导致我们无法一次性得到所有的结果
            Map map = objectMapper.readValue(jsonParam, Map.class);
            searchSourceBuilder = SearchUtil.getSearchSourceBuilder(map);
        }
        searchRequest.source(searchSourceBuilder);
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        long totalHits = response.getHits().totalHits;
        return totalHits;
    }

    @Override
    public Map<String, Integer> statAvg(String receiveStartTime, String receiveEndtTime) throws IOException {
        Map<String, Integer> map = new HashMap<>();
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        searchSourceBuilder.query(QueryBuilders.rangeQuery("receiveTime").from(receiveStartTime, true).to(receiveEndtTime, true));
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("apiNameBieMing").field("apiName");
        aggregationBuilder.subAggregation(AggregationBuilders.avg("avg_totleTime").field("totleTime"));
        searchSourceBuilder.aggregation(aggregationBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        Aggregations aggregations = response.getAggregations();
        ParsedStringTerms apiNameBieMing = aggregations.get("apiNameBieMing");
        List<? extends Terms.Bucket> buckets = apiNameBieMing.getBuckets();
        for (int i = 0; i < buckets.size(); i++) {
            ParsedStringTerms.ParsedBucket bucket = (ParsedStringTerms.ParsedBucket) buckets.get(i);//获取每一条聚合数据
            String keyAsString = bucket.getKeyAsString();//这个key就是最外面的聚合分组后的我们的apiname
            // System.err.println(keyAsString);
            Aggregations bucketAggregations = bucket.getAggregations();//获取聚合的具体内容
            List<Aggregation> list = bucketAggregations.asList();//将具体的内容转成集合
            ParsedAvg aggregation = (ParsedAvg) list.get(0);
            String name = aggregation.getName();//获取子聚合的key
            Double value = aggregation.getValue();//获取平均值
            System.err.println(value);
            map.put(keyAsString, value.intValue());//保存到map中
        }
        return map;

    }


}
